<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>简单线状图II</title>
  <script type="text/javascript" src="http://d3js.org/d3.v5.min.js"></script>
  <script src="../js/d3.tip.js"></script>
</head>
<style>
  .d3-tip {
    background-color: antiquewhite;
    padding: 10px;
    line-height: 30px;
  }

</style>

<body>
  <svg width="1060" height="600" style="background-color: #E8EBEA;"></svg>
</body>
<script>

  const svg = d3.select("svg");
  const width = svg.attr('width');
  const height = svg.attr('height');

  const margin = { top: 60, right: 80, bottom: 60, left: 100 }

  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const data = [
    { time: '00:00', pm25: 75 },
    { time: '01:00', pm25: 66 },
    { time: '02:00', pm25: 43 },
    { time: '03:00', pm25: 32 },
    { time: '04:00', pm25: 20 },
    { time: '05:00', pm25: 18 },
    { time: '06:00', pm25: 16 },
    { time: '07:00', pm25: 33 },
    { time: '08:00', pm25: 53 },
    { time: '09:00', pm25: 66 },
    { time: '10:00', pm25: 55 },
    { time: '11:00', pm25: 67 },
    { time: '12:00', pm25: 99 },
    { time: '13:00', pm25: 138 },
    { time: '14:00', pm25: 110 },
    { time: '15:00', pm25: 99 },
    { time: '16:00', pm25: 119 },
    { time: '17:00', pm25: 125 },
    { time: '18:00', pm25: 173 },
    { time: '19:00', pm25: 168 },
    { time: '20:00', pm25: 162 },
    { time: '21:00', pm25: 143 },
    { time: '22:00', pm25: 132 },
    { time: '23:00', pm25: 87 }
  ]

  const key = d3.map(data[0]).keys()[1];
  const serieArr = data.map(function (d) {
    return {
      key: key,
      time: d3.timeParse('%H:%M')(d.time),
      value: d.pm25
    }
  })
  console.log(serieArr);

  // 如果，stepArray的最大值小于真实数据的最大值，需将真实最大值加上
  // 不然最上面的折线超出背景色，
  const stepArray = d3.ticks(0, d3.max(data, d => d.pm25), 5) // 用于生成背景柱
  const rangeByStep = JSON.parse(JSON.stringify(stepArray));
  console.log(rangeByStep);
  const maxValue = d3.max(data, d => d.pm25);
  if (maxValue > stepArray[stepArray.length - 1]) {
    stepArray.push(maxValue)
  }
  console.log(stepArray);

  const colors = [
    '#6bcd07',
    '#fbd029',
    '#fe8800',
    '#fe0000',
    '#970454',
    '#62001e'
  ] // 用于生成背景柱
  const names = ['优', '良', '轻度污染', '中度污染', '重度污染', '严重污染']

  const xScale = d3
    .scaleTime()
    .range([0, innerWidth])
    .domain([
      serieArr[0].time,
      serieArr[data.length - 1].time
    ]);


  const yScale = d3.scaleLinear()
    .domain([0, d3.max(serieArr, d => {
      return d.value
    })])
    .range([innerHeight, 0]);

  const maingroup = svg.append("g").attr("id", "maingroup")
    .attr("transform", `translate(${margin.left},${margin.top})`);

  /**
   * 动画效果
   */
  maingroup
    .append('defs')
    .append('clipPath') // 添加长方形方块，遮罩作用
    .attr('id', 'clip')
    .append('rect')
    .attr('height', innerHeight)
    .attr('width', 0) // 用遮罩实现线动画
    .transition()
    .duration(1000)
    .attr('width', innerWidth)

  /**
   * 提示框 
   */

  /**
* 提示 
*/
  let tip = d3.tip() // 设置tip
    .attr('class', 'd3-tip')
    .offset([-10, 0])
    .html(function (d) {
      return (
        '<strong>时间：' +
        d3.timeFormat("%H:%M")(d.time) +
        "<br>AQI 值:</strong> <span style='color:red'>" +
        d.value +
        '</span>'
      )
    })
  maingroup.call(tip)


  /**
   * 坐标轴 - y轴
   */
  maingroup.append('g')
    .attr("class", 'axis axis-y')
    .call(d3.axisLeft(yScale).ticks(4))
    .append("text")
    .attr("y", -16)
    .attr('dy', '.71em')
    .attr("text-anchor", 'middle')
    .attr("fill", "red")
    .text("AQI 值");



  /** 
   * 坐标轴 - x轴 
   */
  maingroup.append('g')
    .attr("class", 'axis axis-x')
    .attr('transform', `translate(0,${innerHeight})`) //将x坐标轴从上面移到下面
    .call(d3.axisBottom(xScale).ticks(24).tickFormat(d3.timeFormat("%H:%M")));


  /**
  * 添加竖向的分割线
  */
  //  由于开始位置已默认，不用再设 x,y, 结束位置x1也不变
  //  将所有初始点上移一个innerHeight，那y1 = innerHeight
  maingroup
    .selectAll(".axis-x .tick")
    .append("line")
    .attr("class", "bg-line")
    .attr('stroke', 'rgba(255,255,255,0.5)')
    .attr("stroke-dasharray", '2,2')
    .attr('shape-rendering', 'crispEdges')
    .attr("transform", "translate(0," + -1 * innerHeight + ")")
    .attr('y2', innerHeight)


  /**
  * 添加横向的背景色
  */
  maingroup
    .append("g")
    .attr("class", "bg-bar-v")
    .selectAll("rect")
    .data(rangeByStep)
    .join("rect")
    .attr("x", 1)
    .attr("y", (d, i) => {
      if (i !== rangeByStep.length - 1) {
        return yScale(rangeByStep[i + 1])
      } else {
        return 0;
      }
    })
    .attr("width", innerWidth)
    .attr("height", (d, i) => {
      if(maxValue > d3.max(rangeByStep)){
        return yScale(stepArray[i]) - yScale(stepArray[i + 1])
      }else{
        return yScale(rangeByStep[i]) - yScale(rangeByStep[i + 1])
      }
      
    })
    .attr("fill", (d, i) => {
      return colors[i]
    })
    .attr('stroke', 'none')
    .attr('stroke-width', 0)


  /**
   * 添加背景上的文字
   */
  maingroup
    .append("g")
    .attr('class', 'lineii--bg-bar-text')
    .selectAll(".ylabel")
    .data(rangeByStep)
    .join("text")
    .attr("class", "ylabel")
    .attr("fill", "white")
    .attr("y", (d, i) => {
      if (i < rangeByStep.length - 1) {
        return yScale(d + (rangeByStep[i + 1] - rangeByStep[i]) / 2)
      } else {
        return yScale(d) / 2
      }
    })
    .attr("x", (d, i) => {
      return innerWidth / 2;
    })
    .attr("width", "100")
    .attr("text-anchor", "middle")
    .attr("font-weight", "bold")
    .attr("font-size", "24px")
    .text((d, i) => {
      return names[i]
    })


  /**
   * 折线
   */
  const colorScale = d3.scaleOrdinal(d3.schemeCategory10);

  const line = d3
    .line()
    .x(d => {
      return xScale(d.time);
    })
    .y(d => {
      return yScale(d.value);
    })

  let serie = maingroup
    .selectAll(".line")
    .data([serieArr])
    .join("g")
    .attr("class", 'line');

  serie
    .append("path")
    .attr("d", line)
    .attr('clip-path', 'url(#clip)') //剪切路线
    .attr("class", "line")
    .attr("fill", 'none')
    .style("stroke-width", '3')
    .style('stroke', function (d) {
      return colorScale(d[0].key)
    });

  /**
   * 数字
   */
  let labelg = serie
    .selectAll(".label")
    .data(d => d)
    .join("g")
    .attr("class", "label")
    .attr('transform', function (d, i) {
      return 'translate(' + xScale(d.time) + ',' + yScale(d.value) + ')'
    })

  // 数字背景框
  labelg
    .append("rect")
    .on('mouseover', tip.show)
    .on('mouseout', tip.hide)
    .attr("x", "-15")
    .attr("y", "-15")
    .attr("rx", '5')
    .attr("ry", '5')
    .attr("width", "30")
    .attr("height", "30")
    .attr("fill", "black")
    .style("opacity", "0.5");

  labelg
    .append("text")
    .on('mouseover', tip.show)
    .on('mouseout', tip.hide)
    .attr("fill", "white")
    .attr("y", "5")
    .attr("width", '30')
    .attr("text-anchor", "middle")
    .text(d => d.value)


    /**
     * 添加标题
     */ 
    maingroup
    .append("g")
    .attr("class",'title')
    .append("text")
    .attr("x",innerWidth / 2)
    .attr("y",-10)
    .attr("width",'100')
    .attr("text-anchor","middle")
    .attr("font-size","14px")
    .attr("font-weight","bold")
    .text("XX市昨天PM2.5及空气质量指数(AQI)")

</script>

</html>
